Flyway 数据库迁移工具详解

1. 概述

Flyway 是一款把数据库 Schema 变更(Migration,迁移脚本)纳入版本控制的工具,由 Axel Fontaine 于 2010 年创建,现归 Redgate 公司所有。它将"数据库像代码一样管理"——迁移脚本用 SQL 编写、放进版本控制、按版本顺序执行,让开发、测试、生产各环境的数据库始终保持一致且可追溯,是 Database DevOps(数据库 DevOps)领域事实上的标准工具。

核心思想:数据库是应用的一部分,Schema 变更应当与应用代码一样:版本化(versioned)、可审计(auditable)、可重放(repeatable)、幂等部署(idempotent deploy)。

1.1 Flyway 支持什么

维度说明
客户端形式命令行 CLI、Java API、Maven 插件、Gradle 插件、Spring Boot 自动配置、Docker 镜像、Flyway Desktop(GUI)
数据库PostgreSQL、MySQL/MariaDB、Oracle、SQL Server、SQLite、DB2、H2、MongoDB、Cassandra 等 30+ 种关系型与部分 NoSQL 数据库
迁移格式SQL 脚本(原生数据库语法)、Java 迁移类(复杂数据转换/LOB 处理)、脚本迁移(Groovy/Python 等)
版本层级Community(社区版,核心功能免费)、Teams(团队版,含 undo/自定义校验规则等)、Enterprise(企业版,含 diff/generate/snapshot 等数据库比较与漂移检测)
开源与商业版的区别:Flyway 是开源的,但采用 Open Core(开源核心 + 商业扩展)模式。Community 版的核心引擎(migrate / info / validate / baseline / repair / clean)开源且免费;Teams 与 Enterprise 是闭源商业版,在开源核心之上提供高级插件功能(如 Enterprise 的 diff / generate / model / snapshot / check -drift,Teams 的 undo)。历史背景:Flyway 由 Axel Fontaine 于 2010 年创建时完全开源,2016 年被 Redgate 收购后转为 open core——核心免费开源,靠高级功能商业变现。因此"开源"不等于所有能力免费,字段级结构对比等能力恰好属于闭源的 Enterprise 版。

1.2 专有名词解释

英文术语中文含义
Migration迁移对数据库 Schema 或数据的一次增量变更,通常是一个 SQL 脚本
Versioned Migration版本化迁移带版本号、每个环境只执行一次的迁移(如 V1__create_table.sql)
Repeatable Migration可重复迁移无版本号、文件内容变化(checksum 改变)时重新执行的迁移(如视图、存储过程,R__xxx.sql)
Undo Migration撤销迁移用于回滚对应版本化迁移的脚本(U__xxx.sql,Teams 版)
Baseline基线给一个"已经存在"的数据库打底,把当前状态标记为某个版本,跳过其下所有迁移
Schema History TableSchema 历史表Flyway 自动创建的特殊表,默认 flyway_schema_history,记录迁移的完整审计轨迹
Checksum校验和迁移脚本内容计算的哈希值,用于检测脚本是否被修改
Placeholder占位符脚本中的 ${xxx},执行时被替换,用于区分环境
Callback回调挂在迁移生命周期上的脚本(如 afterMigrate.sql),自动在特定时机执行
Flyway Desktop桌面客户端Redgate 提供的图形化界面,用于开发人员管理迁移脚本与版本控制

2. 核心概念

2.1 迁移类型

Flyway 的迁移分为三种:版本化迁移(Versioned)、可重复迁移(Repeatable)、基线迁移(Baseline)。其中版本化迁移又分为普通迁移和撤销迁移(Undo)。

类型文件前缀执行时机典型用途
版本化迁移V按版本顺序、每个环境只执行一次建表/改表/删表、索引、外键、枚举、UDT;参考数据更新;用户数据修正
可重复迁移R每次 migrate 时,若 checksum 变化则重新执行视图、存储过程、函数、物化视图的(重新)创建;批量参考数据重灌
撤销迁移(Teams+)U通过 undo 命令手动触发撤销对应版本化迁移的副作用
基线迁移B(自动)对已存在数据库执行 baseline 时记录项目中途接入 Flyway,跳过历史遗留脚本

执行顺序规则:在单次 migrate 运行中,先按版本顺序执行所有待执行的版本化迁移,再按描述(description,字母序)执行所有已变更的可重复迁移。

最佳实践:版本化迁移一旦应用到持久化环境就不要修改——需要改动时新建一个更高版本的迁移脚本"向前滚动"(roll forward)。可重复迁移要能幂等执行(多次执行结果一致),通常用 CREATE OR REPLACE。

2.2 命名规范

迁移脚本的文件名由四部分构成,可被配置项调整:

[prefix][version]__[description].[suffix]
 ────────┬───────  ────┬───────   ─────┬─────
         前缀 V/R/U      分隔符 __       后缀 .sql

V1__create_user_table.sql      ← 版本化迁移,版本 1
V2.1__add_age_column.sql       ← 版本支持点号/下划线,如 V2_1 等价
R__all_views.sql               ← 可重复迁移,无版本号
U1__create_user_table.sql      ← 撤销迁移,对应 V1

版本号必须唯一,且按数值排序执行(V1 < V2 < V2.1 < V10)。描述仅用于可读性。用零填充(如 V001)或时间戳可减少多人协作时的冲突。

配置项默认值说明
sqlMigrationPrefixV版本化 SQL 迁移前缀
sqlMigrationSeparator__(双下划线)版本与描述的分隔符
sqlMigrationSuffixes.sqlSQL 迁移后缀,可配多个
repeatableSqlMigrationPrefixR可重复 SQL 迁移前缀
undoSqlMigrationPrefixU撤销 SQL 迁移前缀

2.3 Schema 历史表(flyway_schema_history)

先厘清一个概念:flyway_schema_history 是一张表,不是数据库。 它位于你连接的目标数据库内部(默认在 defaultSchema 中,如 PostgreSQL 的 public.flyway_schema_history)。 Flyway 本身不会创建数据库,只通过 JDBC 连接到已存在的库。

Flyway 只创建一张表——就是这张历史表。数据库里的其他表(users、orders 等应用表)都是迁移脚本执行后产生的,不是 Flyway 创建的。

数据库中各类对象及关系:

对象创建者作用
数据库(Database)人工 / 基础设施(如 Docker、云 RDS)承载所有表,Flyway 只连接、不创建
flyway_schema_history(1 张)Flyway 首次 migrate 自动创建记录"哪个脚本、何时、何人、结果如何"的执行轨迹(审计)
users / orders 等应用表(N 张)迁移脚本(V*.sql / R*.sql)业务数据存储,是脚本执行的"结果"

三者关系(一对多、无外键):

首次 migrate 时,Flyway 会在默认 Schema 中自动创建历史表,作为所有数据库变更的完整审计轨迹。表名可用 table 配置项修改。

字段类型含义
installed_rankint应用顺序排名(主键)
versionvarchar迁移版本号(可重复迁移为空)
descriptionvarchar迁移描述
typevarchar迁移类型(SQL / JDBC / BASELINE / UNDO_SQL 等)
scriptvarchar脚本文件名
checksumint脚本内容校验和(用于 detect 篡改)
installed_byvarchar执行迁移的数据库用户
installed_ontimestamp执行时间
execution_timeint执行耗时(毫秒)
successboolean是否成功

通过 flyway info 命令可以查看每条迁移在目标库上的状态(Migration States):

状态含义
Pending尚未应用,等待 migrate
Success执行成功
Failed执行失败
Missing历史表中有记录但本地找不到对应脚本(脚本被删)
Future本地版本落后于已部署环境(脚本存在于更高版本)
Out of Order乱序应用,重跑整个历史可能得到不同结果
Baseline已通过 baseline 标记为基线
Ignored已忽略,migrate 时不再考虑(repair 后产生)
Deleted被 repair 标记为已删除

2.4 占位符(Placeholders)

所有迁移脚本都支持占位符替换,默认使用 Ant 风格 ${myplaceholder}。常用于抽象环境差异(如表名、所有者、时区等)。

-- V1__create_user_table.sql
CREATE TABLE ${table_prefix}users (
    id BIGINT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);

-- 配置占位符
flyway.placeholders.table_prefix=app_
flyway.placeholders.table_prefix=app_

内置占位符 ${flyway:timestamp} 每次执行都会改变,把它放在可重复迁移顶部,可以让该迁移每次都强制执行(常用于强制刷新物化视图等场景)。

2.5 回调(Callbacks)

把脚本命名为回调事件名并放入回调目录,即可在迁移生命周期的特定时点自动执行。常用于:重编译存储过程、刷新物化视图、VACUUM 等数据库维护。

事件触发时机
beforeMigrate / afterMigratemigrate 前 / 后
beforeEachMigrate / afterEachMigrate每个迁移脚本前 / 后
afterMigrateError迁移失败后
beforeValidate / afterValidatevalidate 前 / 后
beforeClean / afterCleanclean 前 / 后
beforeBaseline / afterBaselinebaseline 前 / 后

2.6 配置方式

Flyway 支持多种配置来源,优先级从高到低:命令行参数 > Java API 编程配置 > 环境变量 > 配置文件(flyway.conf 或 application.yml)。

# flyway.conf 示例
flyway.url=jdbc:postgresql://localhost:5432/demo
flyway.user=postgres
flyway.password=secret
flyway.locations=filesystem:./sql
flyway.baselineOnMigrate=true
flyway.placeholders.env=dev

3. 行为图(系统"做什么")

3.1 用例图 — Flyway 用户交互全景

PlantUML diagram
图 1:Flyway 用例图

3.2 活动图 — migrate 主流程

migrate 是 Flyway 的核心命令:扫描迁移脚本 → 对比数据库已应用记录 → 补齐差距。它是幂等的,无论数据库当前处于哪个版本都可安全执行。

PlantUML diagram
图 2:migrate 主流程活动图

3.3 时序图 — migrate 全过程

一次 flyway migrate 中,Flyway 引擎、迁移脚本与目标数据库之间的交互细节:

PlantUML diagram
图 3:migrate 时序图

3.4 状态机图 — 迁移状态流转

每条迁移在 flyway_schema_history 中会处于不同状态,随着 migrate / repair / baseline 等操作不断流转:

PlantUML diagram
图 4:迁移状态机图

4. 结构图(系统"由什么组成")

4.1 部署图 — 多种接入方式

Flyway 是一个库 + 多种前端的工具:CLI、Java API、构建工具插件、Spring Boot 自动装配、Docker 容器等全部复用同一核心引擎,通过各自配置的 JDBC 驱动连接目标数据库。

PlantUML diagram
图 5:Flyway 部署图

4.2 组件图 — 核心架构

Flyway 核心引擎内部由"配置 → 扫描发现 → 解析 → 执行 → 历史记录"的责任链组成,各组件职责单一、通过接口协作:

PlantUML diagram
图 6:Flyway 核心组件图

4.3 包图 — 代码结构

Flyway 源码(Java)的主要包划分:org.flywaydb 根包下按职责分包,核心引擎与各前端分离:

PlantUML diagram
图 7:Flyway 包图

4.4 类图 — 核心对象模型

核心门面 Flyway 类聚合配置与各能力接口,对外提供 migrate / info / validate 等命令入口:

PlantUML diagram
图 8:Flyway 核心类图

5. 实战演示操作手册

5.1 用 CLI 从零搭建一个项目

以 macOS 上操作 PostgreSQL 为例,完整走一遍"建目录 → 写迁移 → migrate → 改脚本 → 再迁移"的闭环。

步骤 1:下载并安装 Flyway CLI

# 从 https://www.red-gate.com/products/flyway/ 下载最新版(Community 免费)
# 解压后加入 PATH
export PATH="$HOME/flyway-10.x/flyway:$PATH"
flyway -v

步骤 2:创建项目结构与首个迁移

mkdir -p demo-flyway/sql
cd demo-flyway
cat > sql/V1__create_user_table.sql <<'EOF'
CREATE TABLE users (
    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) UNIQUE,
    created_at TIMESTAMP DEFAULT NOW()
);
EOF

步骤 3:配置并执行 migrate

cat > flyway.conf <<'EOF'
flyway.url=jdbc:postgresql://localhost:5432/demo
flyway.user=postgres
flyway.password=secret
flyway.locations=filesystem:./sql
EOF

flyway migrate
# 输出: Successfully applied 1 migration to schema "public" (execution time 00:00.123s)

此时数据库中已自动创建 public.flyway_schema_history 表。查看状态:

flyway info
# ┌──────┬─────────┬─────────────────┬─────┬────────────────────────┬────────┐
# │State │ Version │ Description     │Type │ Script                 │ ...    │
# ├──────┼─────────┼─────────────────┼─────┼────────────────────────┼────────┤
# │ ✔    │ 1       │ create_user...  │ SQL │ V1__create_user_table.sql│ ...   │
# └──────┴─────────┴─────────────────┴─────┴────────────────────────┴────────┘

步骤 4:追加新迁移(推进 schema)

cat > sql/V2__add_profile_column.sql <<'EOF'
ALTER TABLE users ADD COLUMN profile VARCHAR(2000);
EOF

flyway migrate   # 只应用 V2
flyway info      # V1 仍为 Success,V2 变 Success

步骤 5:验证与校验

# 改一下已应用的 V1(比如加个空格),再执行 validate:
flyway validate
# ✗ ERROR: Migration checksum mismatch for migration version 1
#  → applied to database : X, resolved locally : Y

validate 发现 checksum 不一致。若确实要改,用 flyway repair 重新对齐历史表的 checksum:

flyway repair
flyway validate   # 通过
安全提示:clean 会删除配置 Schema 中的所有对象,仅用于开发/测试环境,严禁在生产环境执行。

5.2 集成 Spring Boot

Spring Boot 只需引入依赖 + 配置,应用启动时(JPA 初始化前)自动执行迁移:

<!-- pom.xml -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>
<!-- 数据库驱动按需引入,如 flyway-database-postgresql / mysql-connector-j 等 -->
# application.yml
spring:
  flyway:
    enabled: true
    locations: classpath:db/migration
    baseline-on-migrate: true
  datasource:
    url: jdbc:postgresql://localhost:5432/demo
    username: postgres
    password: secret

把迁移脚本放在 src/main/resources/db/migration/(Spring Boot 默认位置):

src/main/resources/db/migration/
├── V1__create_user_table.sql
└── V2__add_profile_column.sql

启动应用,日志中即可看到 Flyway ... Successfully validated 2 migrations 与 Migrating schema 输出。Maven 构建时也可用插件手动触发:

mvn flyway:migrate    # 默认绑定 pre-integration-test 阶段
gradle flywayMigrate

5.3 已有数据库如何接入 Flyway(Baseline)

项目已运行很久、数据库里已有大量对象,此时接入 Flyway 不能重跑历史脚本,用 baseline 打底:

# 假设现有 Schema 相当于"版本 1"的状态
flyway baseline -baselineVersion=1 -baselineDescription="Existing schema"

# 结果:历史表中插入一条 BASELINE 记录,版本 1,成功
flyway info
# │ Baseline │ 1 │ Existing schema │ BASELINE │ ... │ ✔ │

之后 flyway migrate 只会应用版本 > 1 的迁移(版本 ≤ 1 的脚本被视为 Below Baseline 而跳过)。也可直接开启自动基线:

flyway migrate -baselineOnMigrate=true   # 历史表为空且库非空时自动 baseline 到最新版本

6. 参数指令速查手册

6.1 命令速查

命令层级说明
flyway migrateCommunity把数据库迁移到最新版本(核心命令,幂等)
flyway infoCommunity查看所有迁移及目标库上的状态(Pending/Success/...)
flyway validateCommunity校验本地迁移与已应用记录是否一致(checksum)
flyway baselineCommunity为已有数据库打基线(-baselineVersion / -baselineDescription)
flyway repairCommunity修复历史表:删除失败记录、重新对齐 checksum、删除已删除迁移
flyway cleanCommunity开发专用:删除 Schema 中所有对象(生产禁止!)
flyway undoTeams回滚最近一次已应用的版本化迁移
flyway check -codeCommunity对迁移做代码审查(正则规则 / SQLFluff 集成)
flyway check -dryrunTeams预演迁移会执行哪些 SQL,输出 dry-run 脚本
flyway check -driftEnterprise检测目标库与迁移的漂移(schema drift)
flyway addCommunity按命名规范创建空迁移脚本模板
flyway diff / generate / modelEnterprise数据库比较、自动生成迁移脚本、维护 Schema 模型
flyway snapshotEnterprise对数据库状态做快照,用于漂移检测与回滚
flyway list-enginesCommunity列出支持的数据库引擎及版本
flyway initCommunity初始化新 Flyway 项目(默认配置 + 目录结构)
mvn flyway:migrate—Maven 插件执行迁移(默认阶段 pre-integration-test)
gradle flywayMigrate—Gradle 插件执行迁移

6.2 常用配置参数速查

参数默认值说明
flyway.url—目标数据库 JDBC URL(必填,如 jdbc:postgresql://host:5432/db)
flyway.user / password—数据库连接凭据
flyway.locationsfilesystem:./sql迁移脚本位置,多个用逗号分隔,递归扫描;前缀 filesystem:/classpath:
flyway.schemas默认连接 SchemaFlyway 管理的 Schema 列表(会尝试创建)
flyway.defaultSchemaschemas 第一个历史表所在 Schema
flyway.tableflyway_schema_history历史表名
flyway.createSchemastrue是否自动创建配置的 Schema
flyway.baselineOnMigratefalse非空库且无历史表时是否自动 baseline
flyway.baselineVersion1baseline 打底的版本号
flyway.target最新migrate 目标版本(如 3 只迁到 V3)
flyway.placeholderReplacementtrue是否启用占位符替换
flyway.placeholders.<name>—定义占位符值,${name} 在执行时替换
flyway.sqlMigrationPrefixV版本化迁移前缀
flyway.repeatableSqlMigrationPrefixR可重复迁移前缀
flyway.sqlMigrationSeparator__版本与描述分隔符
flyway.callbacksfilesystem:./sql/callbacks回调脚本目录
flyway.ignoreMigrationPatterns—自定义校验忽略规则(Teams+)

6.3 常见问题排查

症状原因 / 排查方向
Checksum mismatch已应用的迁移脚本被改动。确认是否有意修改:有意则 flyway repair 重新对齐;否则回退脚本改动
Detected resolved migration not applied to database本地多了脚本但库未应用,正常执行 flyway migrate 即可;若脚本被删则 repair
No database found to handle ...缺少对应数据库的 flyway-database-xxx 驱动模块
迁移执行失败、库回滚每个迁移默认包在单个事务中,失败自动回滚;检查 SQL 报错信息后修复脚本,再 migrate
生产误跑 clean无法恢复!务必:禁止在生产配置 clean、用只读账号、CI 中显式禁用
多人同时提交相同版本号迁移冲突。约定使用时间戳版本或 Git 预提交校验,冲突者重编号
Out of Order 警告乱序应用过迁移。确认可接受后,配置 outOfOrder=true 忽略校验